package DuiLie;



// SeqQueue类实现了IQueue接口，用于表示一个循环队列（基于数组实现的队列结构）
public class SeqQueue implements IQueue {

    // 用于存储队列元素的一维数组
    public Object[] elements;

    // 指向队首元素，初始值为0。当有元素出队时，front会相应移动
    public int front;

    // 指向队尾元素的下一个位置，初始值为0。当有元素入队时，rear会相应移动，当rear等于front时队列为空，当(rear + 1) % maxSize == front时队列满
    public int rear;

    // 队列所能容纳的最大元素数量，即数组的长度
    public int maxSize;

    // 构造函数，用于创建一个最大存储单元个数为maxSize的空循环队列
    // 初始化队列的相关属性，包括front、rear以及创建指定大小的数组来存储元素
    public SeqQueue(int maxSize) {
        // 将front指针初始化为0，表示队首元素在数组开头
        this.front = 0;
        // 将rear指针初始化为0，表示队尾元素的下一个位置初始也在数组开头（此时队列为空）
        this.rear = 0;
        // 创建一个大小为maxSize的Object类型数组，用于存储队列中的元素
        this.elements = new Object[maxSize];
        // 设置队列的最大存储单元数量为maxSize
        this.maxSize = maxSize;
    }

    // 将队列置空的方法
    // 通过重置队首指针front和队尾指针rear为初始值0，并可选择将数组中的元素设置为null来实现队列置空
    public void clear() {
        // 将队首指针front重置为0，从逻辑上表示队列中没有元素了，回到初始的空队列状态
        front = 0;
        // 将队尾指针rear重置为0，同样表示队列已为空
        rear = 0;

        // 遍历数组中的每个元素，将其设置为null
        // 这一步是可选的，取决于具体需求。如果希望彻底清理数组占用的内存空间（在某些情况下，比如数组元素是复杂对象且不再使用时），可以这样做
        for (int i = 0; i < maxSize; i++) {
            elements[i] = null;
        }
    }

    // 判断队列是否为空的方法
    // 当front和rear相等时，说明队列中没有元素，即为空队列
    public boolean isEmpty() {
        return this.front == this.rear;
    }

    // 判断队列是否已满的方法
    // 当(rear + 1) % maxSize == front时，队列已满
    public boolean isFull() {
        return (this.rear + 1) % this.maxSize == this.front;
    }

    // 返回队列的长度的方法
    // 通过计算(rear - front + maxSize) % maxSize来得到循环队列的准确长度
    public int length() {
        return (this.rear - this.front + this.maxSize) % this.maxSize;
    }

    // 读取队首元素并返回其值的方法
    // 如果队列为空，会抛出QueueEmptyException异常；否则返回front所指向位置的元素
    public Object peek() throws QueueEmptyException {
        if (isEmpty()) {
            // 当队列为空时，抛出异常并给出相应的错误提示信息
            throw new QueueEmptyException("错误，队列为空");
        }
        // 返回队首元素的值，即数组中front位置的元素
        return elements[this.front];
    }

    // 入队方法，用于将元素x添加到队列的末尾
    // 如果队列已满，会抛出异常；否则将元素放入rear所指向的位置，并将rear指针后移一位以指向新的队尾元素（循环移动）
    public void offer(Object x) throws Exception {
        if (isFull()) {
            throw new Exception("队列已满，无法入队");
        }
        // 将元素x存储到数组中rear所指向的位置
        elements[this.rear] = x;
        // 将rear指针向后移动一位（循环移动），使其指向新的队尾元素位置
        this.rear = (this.rear + 1) % this.maxSize;
    }

    // 出队方法，用于将队首元素从队列中移除并返回该元素的值
    // 如果队列为空，会抛出QueueEmptyException异常；否则移除front所指向位置的元素，并将front指针后移一位（循环移动）
    public Object poll() throws QueueEmptyException {
        if (isEmpty()) {
            // 当队列为空时，抛出异常并给出相应的错误提示信息
            throw new QueueEmptyException("错误，队列为空");
        }
        // 获取front所指向位置的元素，即队首元素
        Object obj = elements[this.front];
        // 将front所指向位置的元素设置为null，表示该元素已从队列中移除
        elements[this.front] = null;
        // 将front指针向后移动一位（循环移动），使其指向新的队首元素位置
        this.front = (this.front + 1) % this.maxSize;
        // 返回被移除的队首元素的值
        return obj;
    }

    // 输出队列中的所有数据元素的方法
    // 如果队列为空，输出相应提示信息并返回；否则遍历队列中的元素并将它们拼接成字符串输出（循环遍历）
    public void display() {
        // 首先判断队列是否为空，如果队列为空，则直接输出提示信息并返回，不再进行后续的遍历操作
        if (isEmpty()) {
            System.out.println("队列为空");
            return;
        }

        // 创建一个StringBuilder对象，用于高效地拼接字符串。
        // 我们将通过遍历队列中的元素，并将它们逐个添加到这个StringBuilder中，最后形成一个完整的表示队列元素的字符串
        StringBuilder sb = new StringBuilder();

        // 先在StringBuilder中添加一个左方括号，作为表示队列元素序列的开始标记
        sb.append("[");

        // 初始化一个索引变量i，使其等于队首指针front的值。
        // 我们将从这个位置开始循环遍历队列中的元素，直到到达队尾元素的位置（注意不是rear本身，因为rear指向队尾元素的下一个位置）
        int i = front;

        // 使用do-while循环来遍历队列中的元素。
        // 这种循环结构至少会执行一次循环体内容，适合我们这里需要先处理一次队首元素的情况
        do {
            // 将当前索引i所指向的数组元素（即队列中的元素）添加到StringBuilder中
            sb.append(elements[i]);

            // 判断当前索引i是否不等于经过循环处理后的(rear - 1)位置。
            // 这里(rear - 1 + maxSize) % maxSize的计算是为了确保在循环队列的场景下，能够正确得到队尾元素的前一个位置。
            // 如果i不等于这个位置，说明还不是队尾元素的前一个位置，那么就在添加的元素后面添加一个逗号和空格，用于分隔不同的元素
            if (i!= (rear - 1 + maxSize) % maxSize) {
                sb.append(", ");
            }

            // 将索引i按照循环队列的规则向后移动一位，即通过取模运算确保i始终在0到maxSize - 1的范围内循环移动
            i = (i + 1) % maxSize;
        } while (i!= rear);

        // 在遍历完所有队列元素后，在StringBuilder中添加一个右方括号，作为表示队列元素序列的结束标记
        sb.append("]");

        // 将StringBuilder中拼接好的表示队列元素的字符串输出到控制台
        System.out.println(sb.toString());
    }
}